//todo : 如何让速度更快
//todo : 如果没有主键怎么办?  --不支持
#include <string>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>

#include "mysql.h"

#include "lktcommon.h"

#define ERR_SUCCESS 0
#define ERR_FAILED  1
#define SQL_MAX_LEN     1024
#define FIELD_MAX_LEN   256
#define INVALID_ROW_SUM (-1)
#define DEFAULT_DBS_HOST "127.0.0.1"
#define DEFAULT_DBS_PORT 6520
#define DEFAUT_DBS_CONCURRENT 1
#define DEFAUT_DBS_QUERYNUM   10000
#define PRIMARY_KEY_MAX_NUM   8
#define INVALID_CHARACTER   "???"

using namespace std;

enum tagCrypt
{
    ENCRYPT = 1,
    DECRYPT = 2 
};

/* 命令行参数 */
typedef struct tagCmdArg
{
    char user[128];
    char passwd[128];
    int securepasswd;
    char host[128];
    int port;
    char database[128];
    char table[128];
    char column[128];
    int crypt; /* 1表示加密;2 表示解密 */
    int concurrent; /* 并发数, 暂不使用 */
    int querynum;    /* 每次query的数量 */
    int verbose;
    int onlyprint;
    char *debugfile;
    FILE *fp;
    char charset[128];
}cmd_arg;

cmd_arg gcmd;

void debug_to_file (const char * format, ...)
{
    if (NULL == gcmd.fp)
    {
        return;
    }

    va_list argp;
    char* arg;

    va_start(argp, format);
    arg = va_arg(argp, char*);

    if (NULL != gcmd.fp)
    {
        fprintf(gcmd.fp, format, arg);
    }
    va_end(argp);

    return;
}

void print_help()
{
    printf("lkt-dr for Linux (x86_64) LOCKet DBS Proxy\n"
            "Copyright (c) 2015-2017, Zenzet. All rights reserved.\n");
    printf("LOCKet online help: <https://www.locket.tt>\n\n");

    printf("This tool is used to refresh history data in database(MySQL).\n");
    printf("You can refresh data between plain text and cipher text.\n\n");

    printf("Usage:  lkt-dr OPTIONS \n\n");
    printf("Options:\n");
    printf("\t-u, \t\tDBS Proxy user \n");
    printf("\t-p, \t\tDBS Proxy password\n");
    printf("\t--sp, \t\tSecurity password\n");
    printf("\t-e, \t\tEncrypt\n");
    printf("\t-d, \t\tDecrypt\n");
    printf("\t--host, \tDBS Proxy host\n");
    printf("\t--port, \tDBS Proxy port (Default DBS Proxy prot is 6520)\n");
    printf("\t--db, \t\tThe database you want to refresh\n");
    printf("\t--table,\tThe table you want to refresh\n");
    printf("\t--column,\tThe column you want to refresh\n");
    //printf("\t--concurrent,\tNumber of connections to refresh, faster than 1 connection (default:1）\n");
    //

    printf("\nOptional:\n");
    //printf("\t--charset,\tThe charset of column (default:UTF8)\n");
    printf("\t--only-print-sql,\n\t\t\tOnly print sql, used to test if sql is correct before refresh real data\n");
    printf("\t--file,\t\tPrint sql redirect to file (default: standard output)\n");
    printf("\t--help, \tLet me help you.\n");

    printf("\nExample:\n");
    printf("\tlkt-dr -uroot -p123456 -e --host=127.0.0.1 --port=6520 --db=testdb --table=testtable --column=testcolumn\n");
    printf("\tlkt-dr -uroot -p123456 -e --db=testdb --table=testtable --column=testcolumn (default host:127.0.0.1 port=6520) \n");
    printf("\tlkt-dr -uroot --sp -e --db=testdb --table=testtable --column=testcolumn (Security password) \n");
    printf("\tlkt-dr -uroot --sp -e --db=testdb --table=testtable --column=testcolumn --file output.txt \n");
}

void print_summary()
{
    {
        printf ("*************Statistics*************\n");
        printf ("     User: %s\n", gcmd.user);
        if (1 == gcmd.securepasswd)
        {
            printf (" Password: %s\n", "******");
        }
        else
        {
            printf (" Password: %s\n", gcmd.passwd);
        }
        printf ("     Host: %s\n", gcmd.host);
        printf ("     Port: %d\n", gcmd.port);
        printf (" Database: %s\n", gcmd.database);
        printf ("    Table: %s\n", gcmd.table);
        printf ("   Column: %s\n", gcmd.column);
        printf ("  Charset: %s\n", gcmd.charset);
        if (ENCRYPT == gcmd.crypt)
        {
            printf ("    Crypt: Encrypt\n");
        }
        else if (DECRYPT == gcmd.crypt)
        {
            printf ("    Crypt: Decrypt\n");
        }
        //printf ("    Conns: %d\n", gcmd.concurrent);
        //printf ("    Query: %d\n", gcmd.querynum);
        if (NULL != gcmd.debugfile)
        {
            printf ("   Output: %s\n", gcmd.debugfile);
        }

        printf ("************************************\n");
    }

    if (NULL != gcmd.debugfile)
    {
        debug_to_file ("*************Statistics*************\n");
        debug_to_file ("     User: %s\n", gcmd.user);
        if (1 == gcmd.securepasswd)
        {
            debug_to_file (" Password: %s\n", "******");
        }
        else
        {
            debug_to_file (" Password: %s\n", gcmd.passwd);
        }
        debug_to_file ("     Host: %s\n", gcmd.host);
        debug_to_file ("     Port: %d\n", gcmd.port);
        debug_to_file (" Database: %s\n", gcmd.database);
        debug_to_file ("    Table: %s\n", gcmd.table);
        debug_to_file ("   Column: %s\n", gcmd.column);
        debug_to_file ("  Charset: %s\n", gcmd.charset);
        if (ENCRYPT == gcmd.crypt)
        {
            debug_to_file ("    Crypt: Encrypt\n");
        }
        else if (DECRYPT == gcmd.crypt)
        {
            debug_to_file ("    Crypt: Decrypt\n");
        }
        //debug_to_file ("    Conns: %d\n", gcmd.concurrent);
        //debug_to_file ("    Query: %d\n", gcmd.querynum);
        if (NULL != gcmd.debugfile)
        {
            debug_to_file ("   Output: %s\n", gcmd.debugfile);
        }
        debug_to_file ("************************************\n");
    }
    
    return;
}

void print_sql(char* sql)
{
    if (1 == gcmd.verbose)
    {
        if (NULL != gcmd.debugfile)
        {
            debug_to_file("SQL:%s\n", sql);
        }
        else
        {
            printf("SQL:%s\n", sql);
        }
    }
    return;
}

int check_cmdline()
{
    int ret = ERR_FAILED;

    do 
    {
        if (0 == strlen(gcmd.user))
        {
            printf ("Error:unknown user value '%s'\n", gcmd.user);
            break;
        }

        if (0 == strlen(gcmd.passwd))
        {
            printf ("Error:unknown password value '%s'\n", gcmd.passwd);
            break;
        }

        if (0 == strlen(gcmd.host))
        {
            printf ("Error:unknown host value '%s'\n", gcmd.host);
            break;
        }

        if (0 == gcmd.port)
        {
            printf ("Error:unknown port value '%d'\n", gcmd.port);
            break;
        }

        if (0 == strlen(gcmd.database))
        {
            printf ("Error:unknown database value '%s'\n", gcmd.database);
            break;
        }

        if (0 == strlen(gcmd.table))
        {
            printf ("Error:unknown table value '%s'\n", gcmd.table);
            break;
        }

        if (0 == strlen(gcmd.column))
        {
            printf ("Error:unknown column value '%s'\n", gcmd.column);
            break;
        }

        if (0 == strlen(gcmd.charset))
        {
            printf ("Error:unknown charset value '%s'\n", gcmd.column);
            break;
        }

        if (0 == gcmd.querynum)
        {
            printf ("Error:unknown querynum value '%s'\n", gcmd.column);
            break;
        }

        if ((ENCRYPT != gcmd.crypt) && (DECRYPT != gcmd.crypt))
        {
            printf ("Error:unknown crypt mode\n");
            break;
        }

        ret = ERR_SUCCESS;
    } while (0);

    return ret;
}

void set_default_cmdline()
{
    if (0 == strlen(gcmd.host))
    {
        snprintf(gcmd.host, sizeof (gcmd.host), "%s", DEFAULT_DBS_HOST);
    }

    if (0 == gcmd.port)
    {
        gcmd.port = DEFAULT_DBS_PORT;
    }

    if (0 == gcmd.concurrent)
    {
        gcmd.concurrent = DEFAUT_DBS_CONCURRENT;
    }

    if (0 == gcmd.querynum)
    {
        gcmd.querynum = DEFAUT_DBS_QUERYNUM;
    }

    if (0 == strlen(gcmd.charset))
    {
        snprintf(gcmd.charset, sizeof (gcmd.charset), "%s", "UTF8");
    }

    gcmd.verbose = 1;

    return;
}

#if 0
int test (MYSQL *mysqlConn, char* sql)
{
    printf("\nsql:%s", sql);
    int ret = mysql_real_query (mysqlConn, sql, strlen(sql));
    if (ERR_SUCCESS != ret)
    {
        ret = ERR_FAILED;
        printf ("mysql_real_query failed. SQL:%s. %s", sql, mysql_error(mysqlConn));

        return ERR_FAILED;
    }

    MYSQL_RES *res = NULL;
    int row_count = 0;
    int field_count = 0;
    do 
    {
        res = mysql_store_result(mysqlConn);
        if (NULL == res)  /* 比如set autocommit = on, 不会返回结果集 */
        {
            printf("result is null");

            field_count = mysql_field_count (mysqlConn);
            printf("field_count:%d", field_count);
            if (0 == field_count)
            {
                row_count = mysql_affected_rows(mysqlConn);
                printf("row_count:%d", row_count);
            }
            else
            {
                printf("Error:%s", mysql_error(mysqlConn));
            }

        }
        else /* select 即使返回empty set，返回值也不会为NULL,只不过rows=0 字段的数量不会为0 */ 
        {   
            printf("result is not null");

            field_count = mysql_field_count(mysqlConn);
            printf("field_count:%d", field_count);

            field_count = mysql_num_fields(res);
            printf("field_count:%d", field_count);

            row_count = mysql_affected_rows(mysqlConn);
            printf("row_count:%d", row_count);

            MYSQL_ROW row = NULL;
            int colIdx = 0;
            //unsigned long *lengths = NULL;

            /* 获取字段名称 */
            MYSQL_FIELD *fields = NULL;
            fields = mysql_fetch_fields (res);

            char buffer[1024] = {0};
            int i = 0;
            for (i = 0; i < field_count; i++)
            {
                strcat (buffer, fields[i].name);
                strcat (buffer, "\t");
            }
            printf("%s", buffer);

            /* 获取每一行数据，每一行由多个字段组成, MYSQL_ROW实际上是char**指针 */
            memset (buffer, 0, sizeof (buffer));
            while (NULL != (row = mysql_fetch_row(res)))
            {
                /* 获取每一行数据，所有字段的长度 */
                //lengths = mysql_fetch_lengths(res);
                for (colIdx = 0; colIdx < field_count; colIdx ++)
                {
                    strcat (buffer, row[colIdx]);
                    strcat (buffer, "\t");
                }
                printf("%s", buffer);
            }
        }
    } while (0);

    mysql_free_result (res);

    return ERR_SUCCESS;
}
#endif

int findPrimaryKey (MYSQL *mysqlConn, char* db, char* table, char primaryKeyList[PRIMARY_KEY_MAX_NUM][FIELD_MAX_LEN], int *primaryKeyNum)
{
    int ret = ERR_SUCCESS;

    char sql[SQL_MAX_LEN] = {0};
    snprintf (sql, sizeof (sql), "select k.column_name from information_schema.table_constraints t join information_schema.key_column_usage k using (constraint_name,table_schema,table_name) where t.constraint_type='primary key' and t.table_schema='%s' and t.table_name='%s'", db, table); 
    print_sql(sql);
    
    ret = mysql_real_query (mysqlConn, sql, strlen(sql));
    if (ERR_SUCCESS != ret)
    {
        ret = ERR_FAILED;
        printf ("Error:mysql_real_query failed. SQL:%s. %s\n", sql, mysql_error(mysqlConn));

        return ret;
    }

    int primayKeyIdx = 0;
    MYSQL_RES *res = NULL;
    res = mysql_store_result(mysqlConn);
    if (NULL != res)
    {
        MYSQL_ROW row  = NULL;
        while (NULL != (row = mysql_fetch_row(res)))
        {
            if (PRIMARY_KEY_MAX_NUM == primayKeyIdx)
            {
                break;
            }
            strncpy (primaryKeyList[primayKeyIdx], row[0], strlen(row[0]));
            primayKeyIdx++;
            *primaryKeyNum = primayKeyIdx;
        }
    }

    mysql_free_result (res);

    if (0 == strlen(primaryKeyList[0]))
    {
        ret = ERR_FAILED;
    }

    return ret;
}

int checkPrimaryKeyConflict (char primaryKeyList[PRIMARY_KEY_MAX_NUM][FIELD_MAX_LEN], int primaryKeyNum, char *column)
{
    int  i = 0;
    int conflict = 0;

    for (i = 0; i < primaryKeyNum; i++)
    {
        if (0 == strcasecmp (primaryKeyList[i], column))
        {
            conflict = true;
        } 
    }

    return conflict;
}

int update_sql(MYSQL *mysqlConn, char* table, char primaryKeyList[PRIMARY_KEY_MAX_NUM][FIELD_MAX_LEN], int primaryKeyNum, MYSQL_ROW row, char *updatefield, int updatefieldIdx)
{
    int ret = ERR_SUCCESS;

    int i = 0;
    string catstring;
    for (i = 0; i < primaryKeyNum; i++)
    {
        catstring.append ("`");
        catstring.append (primaryKeyList[i]);
        catstring.append ("`");
        catstring.append (" = ");
        catstring.append ("'");
        catstring.append (row[i]);
        catstring.append ("'");
        if (i < primaryKeyNum - 1)
        {
            catstring.append (" and ");
        }
    }

    char *updatefieldValue = row[updatefieldIdx];
    if (NULL == updatefieldValue)
    {
        printf ("Error: invalid field value\n");
        return ERR_FAILED;
    }

    /* 比如连接设置成latin1, 但是字段是中文的，那么MySQLAPI收到的字段值会用???表示 */
    /* 遇到这种情况，说明字符集转换失败了，不能再继续执行了*/
    if (0 == strcmp (updatefieldValue, INVALID_CHARACTER))
    {
        printf ("Error: invalid field %s value %s, may be you should change --charset option \n", updatefield, INVALID_CHARACTER);
        return ERR_FAILED;
    }

    char sql[SQL_MAX_LEN] = {0};
    if (ENCRYPT == gcmd.crypt)
    {
        snprintf (sql, sizeof (sql), "update `%s` set `%s`='%s' where %s ", 
                    table, updatefield, updatefieldValue, catstring.c_str()); 
    }
    else if (DECRYPT == gcmd.crypt)
    {
        snprintf (sql, sizeof (sql), "%s update `%s` set `%s`='%s' where %s", 
                    DATA_REFRESH_DECRYPT_IDENTIFY, table, updatefield, updatefieldValue, catstring.c_str()); 
    }
    print_sql(sql);

    if (0 == gcmd.onlyprint)
    {
        ret = mysql_real_query (mysqlConn, sql, strlen(sql));
        if (ERR_SUCCESS != ret)
        {
            ret = ERR_FAILED;
            printf ("Error:mysql_real_query failed. SQL:%s. %s\n", sql, mysql_error(mysqlConn));
            return ret;
        }
    }

    return ret;
}

int get_process_row_sum (MYSQL *mysqlConn, char *table, char* column)
{
    if ((NULL == mysqlConn) || (NULL == table) || (NULL == column))
    {
        printf("invalid param\n");
        return INVALID_ROW_SUM;
    }

    int ret = ERR_SUCCESS;

    char sql[SQL_MAX_LEN] = {0};
    if (ENCRYPT == gcmd.crypt)
    {
        snprintf (sql, sizeof (sql), "select count(*) from `%s` where `%s` not regexp '^%%lkt!.*$'", table, column); 
    }
    else if (DECRYPT == gcmd.crypt)
    {
        snprintf (sql, sizeof (sql), "select count(*) from `%s` where `%s` regexp '^%%lkt!.*$'", table, column); 
    }

    ret = mysql_real_query (mysqlConn, sql, strlen(sql));
    if (ERR_SUCCESS != ret)
    {
        ret = ERR_FAILED;
        printf ("mysql_real_query failed. SQL:%s. %s\n", sql, mysql_error(mysqlConn));

        return INVALID_ROW_SUM;
    }

    long row_sum   = 0;
    MYSQL_ROW row  = NULL;
    MYSQL_RES *res = NULL;
    res = mysql_store_result(mysqlConn);
    if (NULL != res)
    {
        if (NULL != (row = mysql_fetch_row(res)))
        {
            if (NULL != row[0])
            {
               row_sum = strtol (row[0], NULL, 10);
            }
        }
    }
    mysql_free_result (res);

    if (row_sum  < 0 )
    {
        return INVALID_ROW_SUM;
    }

    return row_sum;
}

int data_refresh (MYSQL *mysqlConn)
{
    int ret = ERR_SUCCESS;
    char *table = gcmd.table;
    char *column = gcmd.column;
    char *database = gcmd.database;

    /* 获取行总数 */
    long row_sum = get_process_row_sum (mysqlConn, table, column);
    if (row_sum < 0)
    {
        return ERR_FAILED;
    }

    printf("There are %ld rows need to be refreshed\n", row_sum);
    debug_to_file("There are %ld rows need to be refreshed\n", row_sum);

    if (0 == row_sum)
    {
        printf ("Refresh data finish\n");
        debug_to_file("Refresh data finish\n");
        return ERR_SUCCESS;
    }

    if (ENCRYPT == gcmd.crypt)
    {
        printf ("--------------------------------------------------------------------------------------------------------------------------\n");
        printf("Note:Please confirm the strategy of column '%s' has been enabled before continue, otherwise, the data will not be encrypt\n", column);
        printf ("--------------------------------------------------------------------------------------------------------------------------\n");
    }
    printf("Do you want to continue:(y/n):");
    fflush(stdout);
    char confirm;
    scanf ("%c", &confirm);
    if ('y' != confirm)
    {
        printf("Quit\n");
        return ERR_SUCCESS;
    }

    /* 获取主键 */
    char primaryKeyList[PRIMARY_KEY_MAX_NUM][FIELD_MAX_LEN] = {0};
    int primaryKeyNum = 0;
    ret = findPrimaryKey (mysqlConn, database, table, primaryKeyList, &primaryKeyNum);
    if ((ERR_SUCCESS != ret) || (0 == primaryKeyNum))
    {
        printf("Error:can not find primary key. table '%s' need at least one primary key\n", table);
        return ret;
    }

    printf("Primary key:");
    int i = 0;
    for (i = 0; i < primaryKeyNum; i++)
    {
        printf(" %s\t",  primaryKeyList[i]);
    }
    printf ("\n");

    int conflict = checkPrimaryKeyConflict (primaryKeyList, primaryKeyNum, column);
    if (1 == conflict)
    {
        printf("Error:column '%s' is primary key, can not be used to data refresh\n", column);
        return ERR_FAILED;
    }

    /* 计算开始时间 */
    time_t startT;
    double totalT;
    startT = time(NULL);

    long count = 0;
    int finish = 0;
    char sql[SQL_MAX_LEN] = {0};
    int first = 1;
    do 
    {
        if (1 == first)
        {
            snprintf (sql, sizeof (sql), "set autocommit=on");
            print_sql(sql);

            ret = mysql_real_query (mysqlConn, sql, strlen(sql));
            if (ERR_SUCCESS != ret)
            {
                ret = ERR_FAILED;
                printf ("Error:mysql_real_query failed. SQL:%s. %s\n", sql, mysql_error(mysqlConn));
                break;
            }

            snprintf (sql, sizeof (sql), "set names %s", gcmd.charset);
            print_sql(sql);

            ret = mysql_real_query (mysqlConn, sql, strlen(sql));
            if (ERR_SUCCESS != ret)
            {
                ret = ERR_FAILED;
                printf ("Error:mysql_real_query failed. SQL:%s. %s\n", sql, mysql_error(mysqlConn));
                break;
            }

            first = 0;
        }

        char primayKeyCatString[1024] = {0};
        for (i = 0; i < primaryKeyNum; i++)
        {
            strcat (primayKeyCatString,  "`");
            strcat (primayKeyCatString, primaryKeyList[i]);
            strcat (primayKeyCatString,  "`");
            if (i != primaryKeyNum - 1)
            {
                strcat (primayKeyCatString,  ",");
            }
        }

        if (ENCRYPT == gcmd.crypt)
        {
            snprintf (sql, sizeof (sql), "select %s,`%s`  from `%s` where `%s` not regexp '^%%lkt!.*$' limit %d", 
                    primayKeyCatString, column, table, column, gcmd.querynum); 
        }
        else if (DECRYPT == gcmd.crypt)
        {
            snprintf (sql, sizeof (sql), "select %s,`%s`  from `%s` where `%s` regexp '^%%lkt!.*$' limit %d", 
                    primayKeyCatString, column, table, column, gcmd.querynum); 
        }
        print_sql(sql);

        ret = mysql_real_query (mysqlConn, sql, strlen(sql));
        if (ERR_SUCCESS != ret)
        {
            ret = ERR_FAILED;
            printf ("Error:mysql_real_query failed. SQL:%s. %s\n", sql, mysql_error(mysqlConn));
            break;
        }

        MYSQL_ROW row = NULL;
        MYSQL_RES *res = NULL;
        res = mysql_store_result(mysqlConn);
        if (NULL != res)
        {
            int row_count = mysql_num_rows(res);
            if (0 == row_count)
            {
                finish = 1;
                ret = ERR_SUCCESS;
                printf ("\nRefresh data finish\n");
                debug_to_file("\nRefresh data finish\n");
                break;
            }

            int field_count = mysql_num_fields(res);
            while (NULL != (row = mysql_fetch_row(res)))
            {
                /* 更新 */
                ret = update_sql(mysqlConn, table, primaryKeyList, primaryKeyNum, row, column, field_count - 1);
                if (ERR_SUCCESS != ret)
                {
                    printf("Error:execute sql failed. %s\n", mysql_error(mysqlConn));
                    break;
                }
                printf("\rProcess:             ");
                printf ("\rProcess: %ld/%ld   ", (count + 1) ,  row_sum);
                fflush(stdout);
                count++;
                
                if (count >= row_sum)
                {
                    finish = 1;
                    break;
                }
            }
        }
        else
        {
            mysql_free_result (res);
            ret = ERR_SUCCESS;
            break;
        }
        mysql_free_result (res);

        if (1 == finish)
        {
            break;
        }

        if (ERR_SUCCESS != ret)
        {
            break;
        }
    } while (1);

    time_t endT = time(NULL);
    totalT = difftime(endT, startT);

    int hours = (int) (totalT / 3600);
    int mins = (int) ((totalT - (hours * 3600)) / 60);
    int secs = (int) (totalT - (3600 * hours) - (60 * mins));
    printf("Cost time: %d hours %d minutes %d secs\n", hours, mins, secs);

    return ret;
}

extern char *optarg;
extern int optind, opterr, optopt;
/* 用户名和密码参数 */
/* 首字符为:的时候，当遇到无效选项或者选项缺少参数时，getopt不会输出错误信息 */
const char *optstring = "u:p:ed";   

/* 其他参数 */
static struct option long_options[] = {  
    {"sp", no_argument, NULL, 'm'},  
    {"host", required_argument, NULL, 'h'},  
    {"port",  required_argument, NULL, 'x'},  
    {"db", required_argument, NULL, 's'},  
    {"table", required_argument, NULL, 't'},  
    {"column", required_argument, NULL, 'c'},  
    {"charset", required_argument, NULL, 'r'},  
    {"querynum", required_argument, NULL, 'q'},  
    //{"concurrent", required_argument, NULL, 'o'},  
    {"only-print-sql", no_argument, NULL, 'y'},  
    //{"print-sql", no_argument, NULL, 'v'},  
    {"file", required_argument, NULL, 'f'},  
    {"help",  no_argument,NULL, 'z'},
    {0, 0, 0, 0}  
};  

int main(int argc, char *argv[])
{
    memset (&gcmd, 0 ,sizeof (gcmd));

    int optionCmd = 0;

    /* 检查参数 */
    int option_index = 0; 
    while (-1 != (optionCmd = getopt_long (argc, argv, optstring, long_options, &option_index)))
    {
        switch (optionCmd)
        {
            case 'u':
            {
                if (0 != strlen(optarg))
                {
                    snprintf (gcmd.user, sizeof (gcmd.user), "%s", optarg);
                }

                break; 
            }
            case 'm':
            {
                /* 安全密码 */
                gcmd.securepasswd = 1;
                char* securepasswd = getpass("input password:");
                if (NULL != securepasswd)
                {
                    snprintf (gcmd.passwd, sizeof (gcmd.passwd), "%s", securepasswd);
                }
                break;
            }
            case 'p':
            {
                if (0 != strlen(optarg))
                {
                    snprintf (gcmd.passwd, sizeof (gcmd.passwd), "%s", optarg);
                }

                break;
            }
            case 'e':
            {
                gcmd.crypt = 1;

                break;
            }
            case 'd':
            {
                gcmd.crypt = 2;

                break;
            }
            case 'h':
            {
                if (0 != strlen(optarg))
                {
                    snprintf (gcmd.host, sizeof (gcmd.host), "%s", optarg);
                }

                break;
            }
            case 'x':
            {
                int port = atoi (optarg);
                if (0 != port)
                {
                    gcmd.port = port;
                }

                break;
            }
            case 's':
            {
                if (0 != strlen(optarg))
                {
                    snprintf (gcmd.database, sizeof (gcmd.database), "%s", optarg);
                }

                break;
            }
            case 't':
            {
                if (0 != strlen(optarg))
                {
                    snprintf (gcmd.table, sizeof (gcmd.table), "%s", optarg);
                }

                break;
            }
            case 'c':
            {
                if (0 != strlen(optarg))
                {
                    snprintf (gcmd.column, sizeof (gcmd.column), "%s", optarg);
                }

                break;
            }
            case 'r':
            {
                if (0 != strlen(optarg))
                {
                    snprintf (gcmd.charset, sizeof (gcmd.charset), "%s", optarg);
                }

                break;
            }
            case 'y':
            {
                gcmd.onlyprint = 1;

                break;
            }
#if 0
            case 'o':
            {
                int concurrent = atoi (optarg);
                if (0 != concurrent)
                {
                    gcmd.concurrent = concurrent;
                }

                break;
            }
#endif
            case 'q':
            {
                int querynum = atoi (optarg);
                if (0 != querynum)
                {
                    gcmd.querynum = querynum;
                }

                break;
            }
#if 0
            case 'v':
            {
                gcmd.verbose = 1;
                break;
            }
#endif
            case 'f':
            {
                if (0 != strlen(optarg))
                {
                    gcmd.debugfile = optarg;
                }
                break;
            }
            case 'z':
            {
                print_help ();
                return 0;
            }
            case '?':       /* 无效选项 */
            {
                print_help ();
                return 0;
            }
            case ':':       /* 缺少参数 */
            {
                return 0;
            }
        }
    }

    if ((-1 == optionCmd) && (1 == argc))
    {
        print_help ();
        return 0;
    } 

    /* 设置默认参数 */
    set_default_cmdline();

    /* 检查参数 */
    int ret = check_cmdline ();
    if (ERR_SUCCESS != ret)
    {
        print_help();
        return 0;
    }

    if (NULL != gcmd.debugfile)
    {
        gcmd.fp = fopen(gcmd.debugfile, "w");
        if (NULL == gcmd.fp)
        {
            printf("Error:open %s failed, %s\n", gcmd.debugfile, strerror(errno));
            return 0;
        }
    }

    /* 输出统计信息 */
    print_summary ();

    MYSQL *mysqlConn = NULL;
    do 
    {
        /* 初始化MySQL */
        mysqlConn = mysql_init(NULL);
        if (NULL == mysqlConn)
        {
            printf("mysql init failed\n");
            break;
        }

        /* 连接MySQL */
        if (NULL == mysql_real_connect(mysqlConn, gcmd.host, gcmd.user, gcmd.passwd, gcmd.database, gcmd.port, NULL, 0))
        {
            printf("mysql_real_connect failed, %s\n", mysql_error(mysqlConn));
            break;
        }
        printf("Connect LOCKet DBS server success\n");

        (void) data_refresh(mysqlConn);
    } while (0);

    /* 关闭MySQL连接 */
    mysql_close(mysqlConn);

    if (NULL != gcmd.fp)
    {
        fclose(gcmd.fp);
    }

    return 0;
}
